/**
 * Canny 边缘检测
 * - 标准：
 *  1. 低错误率：意味着只有现有边缘的良好检测。
 *  2. 良好的定位：检测到的边缘像素与实际边缘像素之间的距离必须最小化。
 *  3. 最小响应：每个边缘只有一个检测器响应。
 * - 步骤：
 *  1. 滤除任何噪音（高斯滤波）
 *  2. 求图像强度梯度，查找梯度方向（Sobel）
 *  3. 非最大抑制
 *  4. 双阈值检验
 */

#include <opencv2/imgproc.hpp>
#include <opencv2/highgui.hpp>
#include <iostream>

using namespace cv;

//![variables]
Mat src, src_gray;
Mat dst, detected_edges;

int lowThreshold = 0;
const int max_lowThreshold = 100;
const int ratio = 3;
const int kernel_size = 3;
const char *window_name = "Edge Map";
//![variables]

/**
 * @function CannyThreshold
 * @brief Trackbar callback - Canny thresholds input with a ratio 1:3
 */
static void CannyThreshold(int, void *) {
    //![reduce_noise]
    /// Reduce noise with a kernel 3x3
    blur(src_gray, detected_edges, Size(3, 3));
    //![reduce_noise]

    //![canny]
    /// Canny detector
    Canny(detected_edges, detected_edges, lowThreshold, lowThreshold * ratio, kernel_size);
    //![canny]

    /// Using Canny's output as a mask, we display our result
    //![fill]
    dst = Scalar::all(0);
    //![fill]

    //![copyto]
    src.copyTo(dst, detected_edges);
    //![copyto]

    //![display]
    imshow(window_name, dst);
    //![display]
}


int main(int argc, char *argv[]) {
    //![load]
    CommandLineParser parser(argc, argv, "{@input | lena.jpg | input image}");
    src = imread(samples::findFile(parser.get<String>("@input")), IMREAD_COLOR); // Load an image

    if (src.empty()) {
        std::cout << "Could not open or find the image!\n" << std::endl;
        std::cout << "Usage: " << argv[0] << " <Input image>" << std::endl;
        return -1;
    }
    //![load]

    //![create_mat]
    /// Create a matrix of the same type and size as src (for dst)
    dst.create(src.size(), src.type());
    //![create_mat]

    //![convert_to_gray]
    cvtColor(src, src_gray, COLOR_BGR2GRAY);
    //![convert_to_gray]

    //![create_window]
    namedWindow(window_name, WINDOW_AUTOSIZE);
    //![create_window]

    //![create_trackbar]
    /// Create a Trackbar for user to enter threshold
    createTrackbar("Min Threshold:", window_name, &lowThreshold, max_lowThreshold, CannyThreshold);
    //![create_trackbar]

    /// Show the image
    CannyThreshold(0, 0);

    /// Wait until user exit program by pressing a key
    waitKey(0);

    return 0;
}
